package game;

import io.Packet;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

import javax.persistence.Column;
import javax.persistence.Transient;

import define.ServerDefine;
import map.MapCell;
import map.VMap;
import map.VMapReference;
import player.Player;
import protocol.OpCode;
import protomsg.Map.PlayerMoveMsg2;
import protomsg.Map.UnitRefreshMsg2;
import protomsg.Rpc.ServerID;
import util.CommonTool;
import Delegate.DelegateUtil;
import Delegate.MethodCache;

public abstract class GameObject {
	public VMapReference map;
	public static final byte TYPE_PLAYER = 1;
	public static final byte TYPE_NPC = 2;
	public static final byte TYPE_CREATURE = 3;
	public static final byte TYPE_CORPSE = 4;
	public static final byte TYPE_GATHER = 5;
	public static final byte TYPE_ATTENDANT = 6;
	public static final byte TYPE_MONSTER = 7;
	public static final byte TYPE_SKULL = 8;
	public static final byte TYPE_ITEM = 9;
	public int instanceId; //���е�Player�Ĵ����Զ��������id��ÿ���������Լ���InstanceId����ʹ�����Id����ͬ��
	public byte type;
	public int id;
	@Column(name = "x", nullable = false)
	public int x;
	@Column(name = "y", nullable = false)
	public int y;
	@Transient
	public MapCell mapCell;
	@Transient
	public short moveType; //��������position��Ϣ��Mask������Ҫ����position��Ϣ��ʱ��ֻ�ǰ�λ�����������Ȼ����һ��cycle���ͳһ���͡�һ��cycle���ֻ����һ��position��Ϣ
	
//	 * x							short (��һ��)
//	 * y							short (��һ��) 
//	 * �Ƕ�							byte���Ƕ�/2��(�ڶ���)
//	 * ʱ��							int(��ϵͳ������ʼ����ĺ�����) (�ڶ���)
//	 * �ٶ�							byte(ÿ������أ� (�ڶ���)
//	 * hp�ٷֱ�						byte(200Ϊ��λ)	(������)
//	 * mp�ٷֱ�						byte(200Ϊ��λ) (������)
//	 * state						short (���Ķ�)
//	 * name							string (�����)
//	 * level						byte (�����)
//	 * faction						byte (�����)
	public static final short MOVE_POINT = 1<<7;
	public static final short MOVE_ANGLE = 1<<6;
	public static final short MOVE_HPMP = 1<<5;
	public static final short MOVE_STATE = 1<<4;
	public static final short MOVE_DETAIL = 1<<3;
	
	public static final short MOVE_NAME = 1<<8;
	public static final short MOVE_LEVEL = 1<<9;
	public static final short MOVE_FACTION = 1<<10;
	public static final short MOVE_EQUIPMENT = 1<<11;
	public static final short MOVE_SEX = 1<<12;
	public static final short MOVE_OWNER = 1<<13;
	public static final short MOVE_CLAZZ = 1<<14;
	public static final short MOVE_HORSE = (short)(1<<15);
	public static final short MOVE_ALL = MOVE_POINT|MOVE_ANGLE|MOVE_HPMP|MOVE_STATE|MOVE_DETAIL|MOVE_NAME|MOVE_LEVEL|MOVE_FACTION|MOVE_EQUIPMENT|MOVE_SEX|MOVE_OWNER|MOVE_CLAZZ|MOVE_HORSE;
	protected static final AtomicInteger id_gen = new AtomicInteger(0);
	public final void update()
	{
		Update();
		AnalyseMethodCache(listMethodCaches,true);//ִֻ��һ�εķ���ί��
		AnalyseMethodCache(listRepeatMethodCaches, false);//����ִ�еķ���ί��
	}
	
	/**
	 * �������������б�
	 * @param listDelegate ί�еķ����б�
	 * @param isRemoveToList ִ�к��Ƿ���б����Ƴ�
	 */
	private void AnalyseMethodCache(HashMap<String,MethodCache> listDelegate,boolean isRemoveToList)
	{
		if(listDelegate.size()>0)
		{
			Date nowdDate=new Date();
			ArrayList<String> listRemove=new ArrayList<String>();
			for (Map.Entry<String, MethodCache> methodCache : listDelegate.entrySet())
			{
				if(methodCache.getValue().TimeHasDone(nowdDate))
				{
					methodCache.getValue().Run();
					listRemove.add(methodCache.getKey());
				}
			}
			
			if(isRemoveToList)
			{
				for (String methodCache : listRemove)
				{
					listDelegate.remove(methodCache);
				}
			}
		}
	}
	
	/**
	 * ÿ֡���·���
	 */
	protected abstract void Update();
	
	/**
	 * ���������б�
	 */
	private HashMap<String,MethodCache> listMethodCaches=new HashMap<String,MethodCache>();
	
	/**
	 * ѭ�����������б�
	 */
	private HashMap<String,MethodCache> listRepeatMethodCaches=new HashMap<String,MethodCache>();
	
	/**
	 * ��ʱ���÷���
	 * @param methodName ��������
	 * @param time ��ʱʱ��
	 * @param prams ��������
	 */
	public final void Invoke(String methodName,float time,Object... prams)
	{
		Invoke(methodName, time, 0,listMethodCaches,false, prams);
	}
	
	/**
	 * ��ʱ���÷���
	 * @param methodName ����ί��
	 * @param time ��ʱʱ��
	 * @param prams ��������
	 */
	public final void Invoke(MethodCache methodCache,float time,Object... prams)
	{
		Invoke(methodCache, time, 0, listMethodCaches, false);
	}
	
	/**
	 * ��ʱ���������÷���
	 * @param methodName ��������
	 * @param time ��һ��ִ����ʱʱ��
	 * @param repeatRate ��������ʱ��
	 * @param parms ��������
	 */
	public final void InvokeRepeating(String methodName, float time, float repeatRate,Object... parms)
	{
		Invoke(methodName, time, repeatRate, listRepeatMethodCaches,true, parms);
	}
	
	/**
	 *  ��ʱ���������÷���
	 * @param methodCache ����ί��
	 * @param time ��һ��ִ����ʱʱ��
	 * @param repeatRate ��������ʱ��
	 */
	public final void InvokeRepeating(MethodCache methodCache, float time, float repeatRate)
	{
		Invoke(methodCache, time, repeatRate, listRepeatMethodCaches, true);
	}
	
	/**
	 * ��ʱ���÷���
	 * @param methodName ��������
	 * @param time ��ʱʱ��
	 * @param repeatRate ��������ʱ��
	 * @param listDelegate ί�е��б�
	 * @param isFirstAdd ��һ�����ʱ��Ϊ0ִ����Ϻ��Ƿ����ί���б�
	 * @param prams ��������
	 */
	private final void Invoke(String methodName,float time,float repeatRate,HashMap<String,MethodCache> listDelegate,boolean isFirstAdd,Object... prams)
	{
		
		Method method=DelegateUtil.GetMethod(this.getClass(), methodName, prams);
		MethodCache methodCache=new MethodCache(method, time, repeatRate, prams, this);
		Invoke(methodCache, time, repeatRate, listDelegate, isFirstAdd);
		
	}
	
	/**
	 * ��ʱ���÷���
	 * @param methodCache ����ί��
	 * @param time ��ʱʱ��
	 * @param repeatRate ��������ʱ��
	 * @param listDelegate ί�е��б�
	 * @param isFirstAdd ��һ�����ʱ��Ϊ0ִ����Ϻ��Ƿ����ί���б�
	 */
	private final void Invoke(MethodCache methodCache,float time,float repeatRate,HashMap<String,MethodCache> listDelegate,boolean isFirstAdd)
	{
		if(methodCache!=null&&methodCache.getMethod()!=null)
		{
			if(time<=0)
			{
				try
				{
					methodCache.getMethod().invoke(this, methodCache.getPrams());
					if(isFirstAdd)
					{
						MethodCache addCache=methodCache;
						addCache.setFirst(false);
						listDelegate.put(addCache.getMethod().getName(), addCache);
					}
				} catch (IllegalAccessException e)
				{
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (IllegalArgumentException e)
				{
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (InvocationTargetException e)
				{
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			else
			{
				listDelegate.put(methodCache.getMethod().getName(),methodCache);
			}
		}
	}
	
	/**
	 * ֹͣ������ʱ����
	 */
	public final void CancelInvoke()
	{
		listMethodCaches.clear();
		listRepeatMethodCaches.clear();
	}
	
	/**
	 * ָֹͣ�����Ƶ���ʱ����
	 * @param methodName ��������
	 */
	public final void CancelInvoke(String methodName)
	{
		if(listMethodCaches.containsKey(methodName))
		{
			listMethodCaches.remove(methodName);
		}
		if(listRepeatMethodCaches.containsKey(methodName))
		{
			listRepeatMethodCaches.remove(methodName);
		}
	}
	



	/**
	 * ��ÿ��Cycle��֮��ͳһ����move��Ϣ���ų���p
	 * @param p
	 */
	public void processMove(Player p) 
	{
		//if (moveType != 0) {
			byte[] pt = getMoveMsgData(moveType);
			//broadcast(pt,p,null,false,true,false);
			VMap map = p.getVMap();
			if(map == null)
			{
				return;
			}

			if(map.getMapid().equals(ServerDefine.PVP_MAP_ID) || map.getMapid().equals(ServerDefine.DUEL_MAP_ID) || map.getMapid().equals(ServerDefine.DUEL_MAP_ID1))
			{
				if (map.playerCount == 0) {
					return;
				}
				for (GameObject object : map.instanceid2objects.values()) {
					if (object.type == GameObject.TYPE_PLAYER) 
					{
						Player player = (Player) object;
						//�����͸�������Լ�
						if(player == p)
						{
							continue;
						}
						player.send(pt);
					}
				}
			}
			else
			{
				broadcast(pt,p,null,false,true,false);
			}
			moveType = 0;
		//}
	}
	
	public void processSkill(Player p, byte[] pt)
	{
		broadcast(pt, p, null,false,true,false);
	}
	
	public void processSyncAct(Player p, byte[] pt)
	{
		broadcast(pt, p, null,true,true,false);
	}
	
	public void processDamage(Player p, byte[] pt)
	{
		broadcast(pt, p, null,false,true,false);
	}
	/**
	 * ��һ���ͱ������йص���Ϣ���㲥����Χ���.
	 * @param pt ��Ϣ��
	 * @param p ���ɴ���Ϣ����Դ��ң��������Ϣ����������ң��˲�����null
	 * @param target ����Ϣ�����õ�Ŀ����ң������Ŀ�겻����ң��˲�����null
	 * @param self �Ƿ��͸�����Լ�
	 * @param ingoreParty �Ƿ񲻷��͸����ѣ����ѹ㲥ͨ������ӿڷ��ͣ�
	 * @param isAttack �Ƿ��ǹ�����
	 */
	public void broadcast(byte[] pt, Player p, Player target, boolean self, boolean ingoreParty, boolean isAttack){
		if (mapCell != null) {
			mapCell.broadcast(p, target, pt, self, ingoreParty, isAttack);
		}
	}
	
	public byte[]  getRefreshPacket(boolean visible){
		
		UnitRefreshMsg2.Builder res = UnitRefreshMsg2.newBuilder();
		res.setInstanceId(instanceId);
		
		byte[] sendData = CommonTool.formatSendMsg(ServerID.UNIT_REFRESH_SERVER_VALUE, res.build().toByteArray());
		
		return sendData;
	}
	
	public UnitRefreshMsg2  getRefreshPacket(boolean visible,int type){
		
		UnitRefreshMsg2.Builder res = UnitRefreshMsg2.newBuilder();
		res.setInstanceId(instanceId);
		
		return res.build();
	}
	
	public void getRefreshPacket(Packet pt,boolean visible){
		/*byte b = type;
		if(!visible){
			b |= 1<<7;
		}
		pt.put(b);
		pt.putInt(id);
		pt.putInt(instanceId);*/
	}
	
//	public Packet getMovePacket(short moveType){
//		throw new UnsupportedOperationException();
//	}
	
	public byte[] getMoveMsgData(short moveType)
	{
		byte[] sendData = CommonTool.formatSendMsg(ServerID.PlayerInfo_VALUE, null);
		
		return sendData;
	}
	
	public VMap getVMap(){
		return map.map;
	}
	
	public void removeFromWorld(){
		VMap map = getVMap();
		if(map!=null){
			map.removeGameObject(this, true);
		}
		ObjectAccessor.removeGameObject(this);
	}
	
	public void move(int x, int y) {
		if (this.x != x || this.y != y) {
			VMap v = map.map;
			if (v != null) {
				this.x = x;
				this.y = y;
				if (type != GameObject.TYPE_PLAYER) {/*
					// ����ҽ�ɫ�ҷǾ�̬NPC�����ƶ�ʱ�����ƶ����µĸ��ӣ���ʱԭ�������ܿ�����NPC�ĸ��ӽ�����
					// �ܿ�����NPC����Ҫ��������Щ���������ҷ���ˢû����Ϣ��ͬʱ�����¼�����Ұ���¸��������
					// �ң���Ҫ����ˢ����Ϣ��
					MapCell cell = v.getMapCell(x, y);
					if (cell != null && cell != mapCell) {
						MapCell oldCell = mapCell;
						if (oldCell != null) {
							oldCell.removeGameObject(this);
						}
						mapCell = cell;
						mapCell.addGameObject(this);
						
						// �Ǿ�̬NPC��Ҫ����CELL�ı仯��֪ͨrefresh
						if (!isStatic()) {
    						MapCell[][] diff = MapCell.diff(oldCell, cell);
    						MapCell[] removed = diff[0];
    						if (removed != null) {
    							MapCell.broadcastRefreshNPC(removed, this, false);
    						}
    						MapCell[] added = diff[1];
    						if (added != null) {
    							MapCell.broadcastRefreshNPC(added, this, true);
    						}
						}
					}*/
				} else {
					MapCell cell = v.getMapCell(x, y);
					if (cell != null) {
						if (cell != mapCell) {
							// �����ҽ�ɫ�ƶ����µ�CELL���ˣ���Ҫ�����½�����Ұ��CELL���뿪��Ұ��CELL
							MapCell oldCell = mapCell;
							if (oldCell != null) {
								oldCell.removeGameObject(this);
							}
							mapCell = cell;
							mapCell.addGameObject(this);
							
							// ������ҰCELL�仯
							MapCell[][] diff = MapCell.diff(oldCell, mapCell);
							
							// ���߳���Ұ��CELLˢû
							MapCell[] removed = diff[0];
							if (removed != null) {
								MapCell.broadcastRefreshPlayer(removed, (Player)this, false);
							}

							// ��������Ұ��CELLˢ��
							MapCell[] added = diff[1];
							if (added != null) {
								MapCell.broadcastRefreshPlayer(added, (Player)this, true);
							}
						} else {
							// ���㷨�У����Ӳ��ı�ʱ����ˢ��/ˢû
						}
					}
				}
				/*if((oldX!=x || oldY!=y) && this instanceof Player){
					((Player)this).recordLastAction();
				}*/
			}
		}
	}
}
